package edu.mapi.ir;
/*
    This file is part of ciberRatoToolsSrc.

    Copyright (C) 2001-2008 Universidade de Aveiro

    ciberRatoToolsSrc is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    ciberRatoToolsSrc is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Foobar; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

import java.util.Vector;
import java.util.prefs.Preferences;

import javax.swing.JOptionPane;
import javax.swing.UIManager;

import edu.mapi.ir.ciberIF.ciberIF;
import edu.mapi.ir.controllers.AbstractController;
import edu.mapi.ir.controllers.Actuation;
import edu.mapi.ir.controllers.StopController;
import edu.mapi.ir.state.EstimatedState;


/**
 * MazeWalker is a cibermouse agent that uses a simple subsumption architecture with reactive beahiviors
 * to try to solve the mazes
 */
public class MazeWalker {

	Vector<StateListener> listeners = new Vector<StateListener>();
	
	private double mean = 0;
	private int countCompass = 0;
	
	private int timeToCalibrateCompass = 0;
	
	public void addStateListener(StateListener listener) {
		listeners.add(listener);
	}
	
	public void removeStateListener(StateListener listener) {
		listeners.remove(listener);
	}
	
	private ciberIF cif;
	private EstimatedState state = new EstimatedState();
	private int time = 0;
	
	private AbstractController activeController = new StopController();

	public static void main(String[] args) throws Exception {

		String host, robName;
		int pos; 
		int arg;

		//default values
		robName = "MazeWalker";
		pos = 1;


		try {        
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {  }


		// parse command-line arguments
		try {
			arg = 0;
			while (arg<args.length) {
				if(args[arg].equals("-pos")) {
					if(args.length > arg+1) {
						pos = Integer.valueOf(args[arg+1]).intValue();
						arg += 2;
					}
				}
				else if(args[arg].equals("-robname")) {
					if(args.length > arg+1) {
						robName = args[arg+1];
						arg += 2;
					}
				}
				else if(args[arg].equals("-host")) {
					if(args.length > arg+1) {
						host = args[arg+1];
						arg += 2;
					}
				}
				else throw new Exception();
			}
		}
		catch (Exception e) {
			print_usage();
			return;
		}


		Preferences prefs = Preferences.systemNodeForPackage(MazeWalker.class);
		host = prefs.get("hostname", "localhost");
		String input = JOptionPane.showInputDialog(null, "Please provide the server hostname", host);				
		if (input == null)
			System.exit(0);

		prefs.put("hostname", input);
		host = input;


		// create client
		MazeWalker client = new MazeWalker();

		// register robot in simulator
		client.cif.InitRobot(robName, pos, host);

		// main loop
		client.mainLoop();
	}

	public void start(String host) throws Exception {
		cif.InitRobot("MazeWalker", 0, host);
		mainLoop();
	}

	// Constructor
	public MazeWalker() throws Exception {		
		
		cif = new ciberIF();					
	}

	
	public void calibrateCompass(int time) {
		this.mean = 0;
		this.timeToCalibrateCompass = time;
		this.countCompass = 0;
	}
	
	/** 
	 * reads a new message, decides what to do and sends action to simulator
	 */
	public void mainLoop () {

		Thread t = new Thread() {
			Actuation lastActuation = new Actuation(0,0);

			public void run() {
				cif.ReadSensors();
				while (!cif.GetStartButton()) {
					cif.ReadSensors();	
				}
				
				System.out.println("Competition started!");
				
				while(true) {
					
					Actuation act;
					if (timeToCalibrateCompass > 0 || getActiveController() == null) {						
						act = new StopController().decide(lastActuation, state);
						act.apply(cif);
					}
					else { 
						act = getActiveController().decide(lastActuation, state);
						act.apply(cif);
					}

					lastActuation = act;
					
					cif.ReadSensors();
					
					state.actuate(act.leftPower, act.rightPower);
					
					for (int i = 0; i < 4; i++) {
						
						if(cif.IsObstacleReady(i))
							state.distanceSensors[i] = cif.GetObstacleSensor(i);	
					}
					if (cif.IsCompassReady()) {
						
						if (timeToCalibrateCompass > 0) {
							mean += cif.GetCompassSensor();
							countCompass++;
							timeToCalibrateCompass--;
						
							if (timeToCalibrateCompass == 0)
								state.setRotation(mean/(double)countCompass);
						}	
						state.setExternalCompassValue(cif.GetCompassSensor());
					}
					
					time++;
					
					for (StateListener l : listeners)
						l.stateChanged(state);					
					
					if (cif.GetStopButton()) {
						System.out.println("Competition stopped!");
						while (!cif.GetStartButton()) {
							cif.ReadSensors();	
						}
						System.out.println("Competition started!");
					}
				}
			}
		};

		t.start();

	}

	static void print_usage() {
		System.out.println("Usage: java jClient [-robname <robname>] [-pos <pos>] [-host <hostname>[:<port>]]");
	}

	public EstimatedState getState() {
		return state;
	}

	public void setState(EstimatedState state) {
		this.state = state;
	}

	public int getTime() {
		return time;
	}

	public AbstractController getActiveController() {
		synchronized (activeController) {
			return activeController;	
		}		
	}

	public void setActiveController(AbstractController activeController) {
		synchronized (this.activeController) {
			this.activeController = activeController;	
		}		
	}
}

